using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public class NetManager
{
    public enum  ServerType
    {
        Gateway ,//网关
        Fighter ,//战斗
    }
    /// <summary>
    /// 客户端套接字
    /// </summary>
    private static Socket socket;

    /// <summary>
    /// 字节数组
    /// </summary>
    private static ByteArray byteArray;

    /// <summary>
    /// 消息列表
    /// </summary>
    private static List<MsgBase> msgList;

    /// <summary>
    /// 发送队列
    /// </summary>
    private static Queue<ByteArray> writeQueue;

    /// <summary>
    /// 是否正在链接
    /// </summary>
    private static bool isConnecting;

    /// <summary>
    /// 是否正在关闭
    /// </summary>
    private static bool isClosing;

    /// <summary>
    /// 一帧处理最大消息量
    /// </summary>
    private static int processMsgCount = 10;
    
    /// <summary>
    /// 是否启用心跳机制
    /// </summary>
    private static bool isUsePing = true;

    /// <summary>
    /// 上次发送ping的时间
    /// </summary>
    private static float lastPingTime = 0;
    
    /// <summary>
    /// 上次收到pong的时间
    /// </summary>
    private static float lastPongTime = 0;

    /// <summary>
    /// 心跳机制间隔
    /// </summary>
    private static float pingInterval = 2;

    /// <summary>
    /// udp对象
    /// </summary>
    private static UdpClient udpClient;
        
#region 事件    网络 / 消息
    //网络事件
    public enum NetEvent
    {
        ConnectSucc = 1,
        ConnectFail ,
        Close,
    }

    /// <summary>
    /// 执行的事件
    /// </summary>
    public delegate void EventListener(string err);

    /// <summary>
    /// 事件的字典
    /// </summary>
    private static Dictionary<NetEvent, EventListener> eventListener = new Dictionary<NetEvent, EventListener>();

    /// <summary>
    /// 添加事件
    /// </summary>
    /// <param name="netEvent"></param>
    /// <param name="listener"></param>
    public static void AddEventListener(NetEvent netEvent,EventListener listener)
    {
        if (eventListener.ContainsKey(netEvent))
        {
            eventListener[netEvent] += listener;
        }
        else
        {
            eventListener.Add(netEvent,listener);
        }
    }

    /// <summary>
    /// 移除事件
    /// </summary>
    /// <param name="netEvent"></param>
    /// <param name="listener"></param>
    public static void RemoveListener(NetEvent netEvent,EventListener listener)
    {
        if (eventListener.ContainsKey(netEvent))
        {
            eventListener[netEvent] -= listener;
            if (eventListener[netEvent] == null)
            {
                eventListener.Remove(netEvent);
            }
        }
    }
    
    /// <summary>
    /// 分发事件
    /// </summary>
    /// <param name="netEvent"></param>
    /// <param name="err"></param>
    public static void FireEvent(NetEvent netEvent, string err)
    {
        if (eventListener.ContainsKey(netEvent))
        {
            eventListener[netEvent](err);
        }
    }
    
    /// <summary>
    /// 消息处理委托
    /// </summary>
    public delegate void MsgListener(MsgBase msgBase);

    /// <summary>
    /// 消息事件字典
    /// </summary>
    private static Dictionary<string, MsgListener> msgListeners = new Dictionary<string, MsgListener>();
    
    /// <summary>
    /// 添加事件
    /// </summary>
    /// <param name="msgName"></param>
    /// <param name="listener"></param>
    public static void AddMsgListener(string msgName,MsgListener listener)
    {
        if (msgListeners.ContainsKey(msgName))
        {
            msgListeners[msgName] += listener;
        }
        else
        {
            msgListeners.Add(msgName, listener);
        }
    }
    
    /// <summary>
    /// 移除消息事件
    /// </summary>
    /// <param name="msgName"></param>
    /// <param name="listener"></param>
    public static void RemoveMsgListener(string msgName,MsgListener listener)
    {
        if (msgListeners.ContainsKey(msgName))
        {
            msgListeners[msgName] -= listener;
            if (msgListeners[msgName] == null)
            {
                msgListeners.Remove(msgName);
            }
        }
    }
    
    /// <summary>
    /// 分发事件
    /// </summary>
    /// <param name="msgName"></param>
    /// <param name="msgBase"></param>
    public static void FireMsg(string msgName,MsgBase msgBase)
    {
        if (msgListeners.ContainsKey(msgName))
        {
            msgListeners[msgName](msgBase);
        }
    }
#endregion

    /// <summary>
    /// 初始化
    /// </summary>
    private static void Init()
    {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        byteArray = new ByteArray();
        msgList = new List<MsgBase>();
        writeQueue = new Queue<ByteArray>();
        isConnecting = false;
        isClosing = false;

        lastPingTime = Time.time;
        lastPongTime = Time.time;
    }

    /// <summary>
    /// 链接
    /// </summary>
    /// <param name="ip">ip地址</param>
    /// <param name="port">端口号</param>
    public static void Connect(string ip, int port)
    {
        if (socket != null && socket.Connected)
        {
            Debug.LogError(("链接失败，已经链接过了"));
            return;
        }
        if (isConnecting)
        {
            Debug.LogError("链接失败,正在链接");
            return;
        }
        Init();
        isConnecting = true;
        socket.BeginConnect(ip, port, ConnectCallback, socket);
    }

    /// <summary>
    /// 链接回调
    /// </summary>
    /// <param name="ar"></param>
    private static void ConnectCallback(IAsyncResult ar)
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            socket.EndConnect(ar);
            Debug.Log("Link success ");

            udpClient = new UdpClient((IPEndPoint)socket.LocalEndPoint);
            udpClient.Connect((IPEndPoint)socket.RemoteEndPoint);
            udpClient.BeginReceive(ReceiveUdpCallback, null);
            
            FireEvent(NetEvent.ConnectSucc,"");
            
            isConnecting = false;
            //接收消息
            socket.BeginReceive(byteArray.bytes, byteArray.writeIndex, byteArray.Remain, 0, ReceiveCallback, socket);
        }
        catch (Exception e)
        {
            Debug.LogError("failure in link " + e.Message);
            
            FireEvent(NetEvent.ConnectFail,e.Message);
            
            isConnecting = false;
        }
    }

    /// <summary>
    /// 接收回调
    /// </summary>
    /// <param name="ar"></param>
    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            //接收的数据量
            int count = socket.EndReceive(ar);
            //断开链接
            if (count == 0)
            {
                Close();
                return;
            }

            byteArray.writeIndex += count;

            //处理消息
            OnReceiveData();
            //如果长度过小，扩容
            if (byteArray.Remain < 8)
            {
                byteArray.MoveBytes();
                byteArray.ReSize(byteArray.Length * 2);
            }

            socket.BeginReceive(byteArray.bytes, byteArray.writeIndex, byteArray.Remain, 0, ReceiveCallback, socket);
        }
        catch (SocketException e)
        {
            Debug.LogError("Receive failed " + e.Message);
            throw;
        }
    }

    /// <summary>
    /// 关闭客户端
    /// </summary>
    private static void Close()
    {
        if (socket == null || !socket.Connected)
        {
            return;
        }
        if (isConnecting )
        {
            return;
        }

        //消息没发送完
        if (writeQueue.Count > 0)
        {
            isClosing = true;
        }
        else
        {
            socket.Close();
            FireEvent(NetEvent.Close,"");
        }
    }

    /// <summary>
    /// 处理接收的消息
    /// </summary>
    private static void OnReceiveData()
    {
        if (byteArray.Length <= 2)
        {
            return;
        }
        byte[] bytes = byteArray.bytes;
        int readIndex = byteArray.readIndex;
        //解析消息总体的长度
        short length = (short)(bytes[readIndex + 1] << 8 | bytes[readIndex]);

        if (byteArray.Length < length + 2)
        {
            return;
        }
        byteArray.readIndex += 2;
        int nameCount = 0;
        string protoName = MsgBase.DecodeName(byteArray.bytes, byteArray.readIndex, out nameCount);
        if (protoName == "")
        {
            Debug.LogError("Protocol name Parsing failure!");
            return;
        }

        byteArray.readIndex += nameCount;

        //解析协议体
        int bodyLength = length - nameCount;
        MsgBase msgBase = MsgBase.Decode(protoName, byteArray.bytes, byteArray.readIndex, bodyLength);
        byteArray.readIndex += bodyLength;

        //移动数据
        byteArray.MoveBytes();
        lock (msgList)
        {
            msgList.Add(msgBase);
        }

        if (byteArray.Length > 2)
        {
            OnReceiveData();
        }
    }

    /// <summary>
    /// 发送协议
    /// </summary>
    /// <param name="msg"></param>
    public static void Send(MsgBase msg, ServerType serverType)
    {
        if (socket == null || !socket.Connected)
        {
            return;
        }

        if (isConnecting)
        {
            return;
        }

        if (isClosing)
        {
            return;
        }
        
        //编码
        byte[] nameBytes = MsgBase.EncodeName(msg);
        byte[] bodyBytes = MsgBase.Encode(msg);
        short len = (short)(nameBytes.Length + bodyBytes.Length + 1);
        byte[] sendBytes = new byte[len + 2];
        sendBytes[0] = (byte)(len & 0x00ff);
        sendBytes[1] = (byte)(len >> 8);
        sendBytes[2] = (byte)(serverType);
        Array.Copy(nameBytes, 0, sendBytes, 3, nameBytes.Length);
        Array.Copy(bodyBytes, 0, sendBytes, nameBytes.Length + 3, bodyBytes.Length);

        ByteArray ba = new ByteArray(sendBytes);
        int count = 0;
        lock (writeQueue)
        {
            writeQueue.Enqueue(ba);
            count = writeQueue.Count;
        }

        if (count == 1)
        {
            socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallback, socket);
        }
    }

    /// <summary>
    /// 发送回调
    /// </summary>
    /// <param name="ar"></param>
    private static void SendCallback(IAsyncResult ar)
    {
        Socket socket = (Socket)ar.AsyncState;
        if (socket == null || !socket.Connected)
        {
            return;
        }
        int count = socket.EndSend(ar);
        ByteArray ba;
        lock (writeQueue)
        {
            ba = writeQueue.First();
        }

        ba.readIndex += count;
        if (ba.Length == 0)
        {
            lock (writeQueue)
            {
                writeQueue.Dequeue();
                ba = writeQueue.First();
            }
        }

        //没有发送完成，还有消息要继续发送
        if (ba != null)
        {
            socket.BeginSend(ba.bytes, ba.readIndex, ba.Length, 0, SendCallback, socket);
        }
        
        if (isClosing)
        {
            socket.Close();
        }
    }

    /// <summary>
    /// 处理消息
    /// </summary>
    private static void MsgUpdate()
    {
        if (msgList.Count == 0)
        {
            return;
        }

        for (int i = 0; i < processMsgCount; i++)
        {
            MsgBase msgBase = null;
            lock (msgList)
            {
                if (msgList.Count > 0)
                {
                    msgBase = msgList[0];
                    msgList.RemoveAt(0);
                }
            }

            if (msgBase != null)
            {
                FireMsg(msgBase.ProtoName,msgBase);
            }
            else
            {
                break;
            }
        }
    }

    // private static void PingUpdate()
    // {
    //     if (!isUsePing)
    //     {
    //         return;
    //     }
    //
    //     if (Time.time - lastPingTime > pingInterval)
    //     {
    //         MsgPing msg = new MsgPing();
    //         Send(msg);
    //         lastPingTime = Time.time;
    //     }
    //     
    //     //断开的处理
    //     if (Time.time - lastPongTime > pingInterval * 4)
    //     {
    //         Close();
    //     }
    // }

    public static void Update()
    {
        MsgUpdate();
    }

#region Udp

    private static void ReceiveUdpCallback(IAsyncResult ar)
    {
        IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
        byte[] bytes = udpClient.EndReceive(ar, ref ipEndPoint);

        int nameCount = 0;
        string protoName = MsgBase.DecodeName(bytes, 0, out nameCount);
        if (protoName == "")
        {
            Debug.LogError("udp接收解析失败");
            return;
        }

        int bodyCount = bytes.Length - nameCount;

        MsgBase msgBase = MsgBase.Decode(protoName, bytes, nameCount, bodyCount);
        lock (msgList)
        {
            msgList.Add(msgBase);
        }

        udpClient.BeginReceive(ReceiveUdpCallback, null);
    }

    public static void SendTo(MsgBase msgBase,ServerType serverType)
    {
        byte[] nameBytes = MsgBase.EncodeName(msgBase);
        byte[] bodyBytes = MsgBase.Encode(msgBase);
        int len = (nameBytes.Length + bodyBytes.Length + 1);
        byte[] sendBytes = new byte[len];
        sendBytes[0] = (byte)serverType;

        Array.Copy(nameBytes, 0, sendBytes, 1, nameBytes.Length);
        Array.Copy(bodyBytes, 0, sendBytes, 1 + nameBytes.Length, bodyBytes.Length);
        udpClient.Send(sendBytes, sendBytes.Length);
    }
#endregion
}